// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_OBJECTS_SLOTS_ATOMIC_INL_H_
#define V8_OBJECTS_SLOTS_ATOMIC_INL_H_

#include "src/base/atomic-utils.h"
#include "src/objects/compressed-slots.h"
#include "src/objects/slots.h"

namespace v8 {
namespace internal {

    // This class is intended to be used as a wrapper for elements of an array
    // that is passed in to STL functions such as std::sort. It ensures that
    // elements accesses are atomic.
    // Usage example:
    //   FixedArray array;
    //   AtomicSlot start(array->GetFirstElementAddress());
    //   std::sort(start, start + given_length,
    //             [](Tagged_t a, Tagged_t b) {
    //               // Decompress a and b if necessary.
    //               return my_comparison(a, b);
    //             });
    // Note how the comparator operates on Tagged_t values, representing the raw
    // data found at the given heap location, so you probably want to construct
    // an Object from it.
    class AtomicSlot : public SlotBase<AtomicSlot, Tagged_t> {
    public:
        // This class is a stand-in for "Address&" that uses custom atomic
        // read/write operations for the actual memory accesses.
        class Reference {
        public:
            explicit Reference(Tagged_t* address)
                : address_(address)
            {
            }
            Reference(const Reference&) V8_NOEXCEPT = default;

            Reference& operator=(const Reference& other) V8_NOEXCEPT
            {
                AsAtomicTagged::Relaxed_Store(
                    address_, AsAtomicTagged::Relaxed_Load(other.address_));
                return *this;
            }
            Reference& operator=(Tagged_t value)
            {
                AsAtomicTagged::Relaxed_Store(address_, value);
                return *this;
            }

            // Values of type AtomicSlot::reference must be implicitly convertible
            // to AtomicSlot::value_type.
            operator Tagged_t() const { return AsAtomicTagged::Relaxed_Load(address_); }

            void swap(Reference& other)
            {
                Tagged_t tmp = value();
                AsAtomicTagged::Relaxed_Store(address_, other.value());
                AsAtomicTagged::Relaxed_Store(other.address_, tmp);
            }

            bool operator<(const Reference& other) const
            {
                return value() < other.value();
            }

            bool operator==(const Reference& other) const
            {
                return value() == other.value();
            }

        private:
            Tagged_t value() const { return AsAtomicTagged::Relaxed_Load(address_); }

            Tagged_t* address_;
        };

        // The rest of this class follows C++'s "RandomAccessIterator" requirements.
        // Most of the heavy lifting is inherited from SlotBase.
        using difference_type = int;
        using value_type = Tagged_t;
        using reference = Reference;
        using pointer = void*; // Must be present, but should not be used.
        using iterator_category = std::random_access_iterator_tag;

        AtomicSlot()
            : SlotBase(kNullAddress)
        {
        }
        explicit AtomicSlot(Address address)
            : SlotBase(address)
        {
        }
        explicit AtomicSlot(ObjectSlot slot)
            : SlotBase(slot.address())
        {
        }

        Reference operator*() const
        {
            return Reference(reinterpret_cast<Tagged_t*>(address()));
        }
        Reference operator[](difference_type i) const
        {
            return Reference(reinterpret_cast<Tagged_t*>(address() + i * kTaggedSize));
        }

        friend void swap(Reference lhs, Reference rhs) { lhs.swap(rhs); }

        friend difference_type operator-(AtomicSlot a, AtomicSlot b)
        {
            return static_cast<int>(a.address() - b.address()) / kTaggedSize;
        }
    };

} // namespace internal
} // namespace v8

#endif // V8_OBJECTS_SLOTS_ATOMIC_INL_H_
